/************************************************************************/
/* 
*Auther: Henry
*Create Date: 19-01-2005 
*Modify Date: 04-02-2005
*Version: 1.1
*Description: Code and decode a file.
                                                                     */
/************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define DISTURB_PARAM (13)
#define FROST_FILENAME_EXTENTION "zll"
#define MAX_PASSWORD_LENTH (25)
#define MIN_PASSWORD_LENTH (4)
#define MAX_FILE_NAME_LEN (30)
#define MAX_FILE_LEN (0x40000000)
#define MAX_SECTION_LEN (0x00800000)
#define FROST_FILE_HEAD_SIZE (0x0100)
#define MAX_STUFF_LEN (FROST_FILE_HEAD_SIZE - MAX_PASSWORD_LENTH - 9)
#define PASSWORD_CHECK_TIMES (4)

//local functions
unsigned char dirty(unsigned char bits);
unsigned char clear(unsigned char bits);
int code(void);
int code_kernel(char *file_copy, unsigned int num);
int decode(void);
int decode_kernel(unsigned char *uc_str, unsigned int num);
int disturb(unsigned char *file_copy, unsigned int num);
int adjust(unsigned char* file_copy, unsigned int num);

unsigned char *stuff = "Seetheleaf,twopartsunited,Forsolong,millionsofyears";//short than MAX_STUFF_LEN

/*
 *	code file
 */
int code(void)
{
	short int count;
	FILE *fp_src;
	FILE *fp_frost;
	
	unsigned int num = 0;
	unsigned int last_section_len;
	unsigned int i = 0;

	unsigned char src_file_name_len;
	char src_file_name[MAX_FILE_NAME_LEN+1];
	char frost_file_name[MAX_FILE_NAME_LEN+1];	
	unsigned char password_len;
	unsigned char password[MAX_PASSWORD_LENTH + 1];

	unsigned char frost_file_head[FROST_FILE_HEAD_SIZE];
	unsigned char *file_copy;

	//open a binary file for reading.
	printf("Please enter the file name, which you want to code.\n>:");
	scanf("%s", src_file_name);
	fp_src = fopen(src_file_name, "rb");
	if (fp_src == NULL) {
		printf("File doesn't exist or can't be opened!\nRemember to enter the filename extention.\n");
		return 1;
	}
	
	//Open a binary file for writing.
	src_file_name_len = strlen(src_file_name);
	strcpy(frost_file_name, src_file_name);
	strcpy(frost_file_name+src_file_name_len-3, FROST_FILENAME_EXTENTION);	//3: length of filename entention.
	fp_frost = fopen(frost_file_name, "a+b");
	if (fp_frost == NULL) {
		printf("File can't be created!\n");
		fclose(fp_src);
		fp_src = NULL;
		return 1;
	}

	//get src file data num, and check it.
	if (0 != fseek(fp_src, 0, SEEK_END)) {
		printf("Can't find the file's end!\n");
		fclose(fp_src);
		fp_src = NULL;
		fclose(fp_frost);
		fp_frost = NULL;
		return 1;
	}
	num = ftell(fp_src);
	if (num < 0) {
		printf("Can't get the file's length!\n");
		fclose(fp_src);
		fp_src = NULL;
		fclose(fp_frost);
		fp_frost = NULL;
		return 1;
	}
	if (num > MAX_FILE_LEN) {
		printf("File is too big!\n");
		fclose(fp_src);
		fp_src = NULL;
		fclose(fp_frost);
		fp_frost = NULL;
		return 1;
	}

	//get password contained in frost file head.
	printf("Please enter your password. %d-%d Bytes length can be accepted.\n>:", MIN_PASSWORD_LENTH, MAX_PASSWORD_LENTH);
	while (1) {
		scanf("%s", password);
		password_len = strlen(password);
		if (password_len < MIN_PASSWORD_LENTH) {
		printf("Failed! Enter a longer password.\n>:");
		}
		else if (password_len > MAX_PASSWORD_LENTH) {
			printf("Failed! Enter a shorter password.\n>:");
		}
		else
			break;
	}

	if (num > MAX_SECTION_LEN) {
		printf("Waiting...");
	}

	//make frost file head. see file "readme.txt"
	memcpy(frost_file_head, src_file_name+src_file_name_len-3, 3);	//3: length of filename entention.
	strcpy(frost_file_head+3, password);
	strcpy(frost_file_head+3+MAX_PASSWORD_LENTH, stuff);
	frost_file_head[3+MAX_PASSWORD_LENTH+MAX_STUFF_LEN] = 0;	//section number
	frost_file_head[3+MAX_PASSWORD_LENTH+MAX_STUFF_LEN+1] = 0;	//last section number
	for(count=0; count<4; count++){		
		frost_file_head[3+MAX_PASSWORD_LENTH+MAX_STUFF_LEN+1+1+count] = ((num<<(count*8)) & 0xff000000) >> 24;
	}

	//code frost file head.
	if (0 != code_kernel(frost_file_head, FROST_FILE_HEAD_SIZE)) {
		fclose(fp_src);
		fp_src = NULL;
		fclose(fp_frost);
		fp_frost = NULL;
		return 1;
	}

	//save head data to frost file	
	if (FROST_FILE_HEAD_SIZE != fwrite(frost_file_head, sizeof(unsigned char), FROST_FILE_HEAD_SIZE, fp_frost)) {
		fclose(fp_src);
		fp_src = NULL;
		fclose(fp_frost);
		fp_frost = NULL;
		return 1;
	}
	
	//buffer of one section. 
	file_copy = (unsigned char *)malloc(MAX_SECTION_LEN * sizeof(unsigned char));
	if (file_copy == NULL) {
		printf("Memory resource is not enough!\n");
		fclose(fp_src);
		fp_src = NULL;
		fclose(fp_frost);
		fp_frost = NULL;
		return 1;
	}
	
	//read, code and save datas which is divided into sections.
	rewind(fp_src);
	for(i=0; i<num/MAX_SECTION_LEN; i++){
		//read one section from src file to memory.
		if (MAX_SECTION_LEN != fread(file_copy, sizeof(unsigned char), MAX_SECTION_LEN, fp_src)) {
			free(file_copy);
			file_copy = NULL;
			fclose(fp_src);
			fp_src = NULL;
			fclose(fp_frost);
			fp_frost = NULL;
			return 1;
		}	
		//code section.
		if (0 != code_kernel(file_copy, MAX_SECTION_LEN)) {
			free(file_copy);
			file_copy = NULL;
			fclose(fp_src);
			fp_src = NULL;
			fclose(fp_frost);
			fp_frost = NULL;
			return 1;
		}
		//write section into frost file.
		if (MAX_SECTION_LEN != fwrite(file_copy, sizeof(unsigned char), MAX_SECTION_LEN, fp_frost)) {
			free(file_copy);
			file_copy = NULL;
			fclose(fp_src);
			fp_src = NULL;
			fclose(fp_frost);
			fp_frost = NULL;
			return 1;
		}		
	}
	last_section_len = num%MAX_SECTION_LEN;
	if (last_section_len != 0) {
		//read last section from src file to memory.
		if (last_section_len != fread(file_copy, sizeof(unsigned char), last_section_len, fp_src)) {
			free(file_copy);
			file_copy = NULL;
			fclose(fp_src);
			fp_src = NULL;
			fclose(fp_frost);
			fp_frost = NULL;
			return 1;
		}		
		//code last section.
		if (0 != code_kernel(file_copy, last_section_len)) {
			free(file_copy);
			file_copy = NULL;
			fclose(fp_src);
			fp_src = NULL;
			fclose(fp_frost);
			fp_frost = NULL;
			return 1;
		}
		//write last section into frost file.
		if (last_section_len != fwrite(file_copy, sizeof(unsigned char), last_section_len, fp_frost)) {
			free(file_copy);
			file_copy = NULL;
			fclose(fp_src);
			fp_src = NULL;
			fclose(fp_frost);
			fp_frost = NULL;
			return 1;
		}		
	}

	free(file_copy);
	file_copy = NULL;
	fclose(fp_src);
	fp_src = NULL;
	fclose(fp_frost);
	fp_frost = NULL;
	return 0;
}

int code_kernel(char *file_copy, unsigned int num)
{
	unsigned char *buffer;
	unsigned int i;

	buffer = file_copy;
	for(i=0; i<num; i++){
		*buffer = dirty(*buffer);
		buffer++;
	}
	buffer = NULL;

	if (0 != disturb(file_copy, num)) {
		return 1;
	}
	return 0;
}

/*
disturb bits's original position in the file. 
divide the file into DISTURB_PARAM teams, and then link them.
*/
int disturb(unsigned char *file_copy, unsigned int num)
{
	unsigned int i;
	short int j;
	short int account;
	short int remainder;
	unsigned int quotient;

	unsigned int bit_team;
	unsigned int start_position_of_team[DISTURB_PARAM];
	unsigned int bit_position_before;
	unsigned int bit_position_after;
	unsigned char bit_value;

	unsigned char *buffer;

	buffer = (unsigned char *)malloc(sizeof(unsigned char) * num);
	if (buffer == NULL) {
		return 1;
	}
	memset(buffer, 0, num);
	
	//get the start positions of the new teams
	quotient = num*8/DISTURB_PARAM;
	remainder = num*8%DISTURB_PARAM;
	for(account=0; account<DISTURB_PARAM; account++){
		if (account < remainder) {
			start_position_of_team[account] = account + account*quotient;
		}
		else{
			start_position_of_team[account] = remainder + account*quotient;
		}
	}
	
	//put every bit in new position
	for(i=0; i<num; i++){
		for(j=0; j<8; j++ ){
			bit_position_before = (i*8+j);			
			bit_team = bit_position_before%DISTURB_PARAM;
			bit_position_after = start_position_of_team[bit_team] + (bit_position_before/DISTURB_PARAM);
			bit_value = (file_copy[i] << j) & (0x80);//put the bit at the front of a char: b*******
			buffer[bit_position_after/8] |= bit_value >> (bit_position_after%8);		
		}
	}

	memcpy(file_copy, buffer, num);//void *memcpy( void *dest, const void *src, size_t count );
	free(buffer);
	buffer = NULL;

	return 0;

}

/*
 *76543210-->02467531
 */
unsigned char dirty(unsigned char bits)
{
	unsigned char temp1 = 0;//0246****
	unsigned char temp2 = 0;//****7531
	int i;

	for(i=0; i<8; i++){
		if (i%2 == 0) {
			temp1 |= ((bits>>i) & 0x01) << (7-i/2);
		}
		else{
			temp2 |= ((bits>>i) & 0x01) << (i/2);
		}
	}	
	return (temp1 | temp2);
}

/*
 *	opposite to code().
 */
int decode(void)
{
	short int count;
	unsigned int data_num = 0;
	unsigned int last_section_len;
	unsigned int i = 0;
	FILE *fp_frost;
	FILE *fp_dest;
	unsigned char *data_copy;
	unsigned char frost_file_head[FROST_FILE_HEAD_SIZE];
	unsigned char frost_file_name_len;
	unsigned char frost_file_name[MAX_FILE_NAME_LEN + 1];
	unsigned char password[MAX_PASSWORD_LENTH + 1];	
	unsigned char password_to_check[MAX_PASSWORD_LENTH + 1];	

	//open a frost file.
	printf("Please enter the file name, which you want to decode.\n>:");
	scanf("%s", frost_file_name);
	fp_frost = fopen(frost_file_name, "rb");
	if (fp_frost == NULL) {
		printf("File doesn't exist, or can't be opened!\n");
		return 1;
	}

	//read frost file head into memory, and decode it.
	if (FROST_FILE_HEAD_SIZE != fread(frost_file_head, sizeof(unsigned char), FROST_FILE_HEAD_SIZE, fp_frost)) {
		fclose(fp_frost);
		fp_frost = NULL;
		return 1;
	}
	if (0 != decode_kernel(frost_file_head, FROST_FILE_HEAD_SIZE)) {
		fclose(fp_frost);
		fp_frost = NULL;
		return 1;
	}

	//get elements contained in the frost file head
	frost_file_name_len = strlen(frost_file_name);
	for(count=0; count<3; count++){
		frost_file_name[frost_file_name_len-3+count] = frost_file_head[count];
	}	//3: filename extention length.
	memcpy(password, frost_file_head+3, MAX_PASSWORD_LENTH);
	password[MAX_PASSWORD_LENTH] = '\0';
	data_num = 0;
	for(count=0; count<4; count++){
		data_num |= (unsigned int)(frost_file_head[FROST_FILE_HEAD_SIZE-count-1]) << count*8;
	}
	
	//check data number
	if (data_num > MAX_FILE_LEN) {
		printf("File is too big!\n");
		fclose(fp_frost);
		fp_frost = NULL;
		return 1;
	}

	//check password
	count = 0;
	while (1) {
		count++;
		if (count > PASSWORD_CHECK_TIMES) {
			printf("Why can't you remember the password? Ask the author for help.\n");
			fclose(fp_frost);
			fp_frost = NULL;
			return 1;
		}
		printf("Please enter your password.\n>:");
		scanf("%s", password_to_check);
		if (0 == strcmp(password, password_to_check)) {
			break;
		}
		else{
			printf("Wrong password!\nYou have %d times left.\n", PASSWORD_CHECK_TIMES-count);
		}
	}
	
	if (data_num > MAX_SECTION_LEN) {
		printf("Waiting...");
	}

	//open a binary file for writing.
	fp_dest = fopen(frost_file_name, "a+b");
	if (fp_dest == NULL) {
		fclose(fp_frost);
		fp_frost = NULL;
		return 1;
	}

	//buffer for section.
	data_copy = (unsigned char *)malloc(MAX_SECTION_LEN * sizeof(unsigned char));
	if (data_copy == NULL) {
		fclose(fp_frost);
		fp_frost = NULL;
		fclose(fp_dest);
		fp_dest = NULL;
		return 1;
	}

	//read, code and save sections.
	for(i=0; i<data_num/MAX_SECTION_LEN; i++){
		//read frost file section to memory.
		if (MAX_SECTION_LEN != fread(data_copy, sizeof(unsigned char), MAX_SECTION_LEN, fp_frost)) {
			free(data_copy);
			data_copy = NULL;
			fclose(fp_frost);
			fp_frost = NULL;
			fclose(fp_dest);
			fp_dest = NULL;
			return 1;
		}
		//decode the section.
		if (0 != decode_kernel(data_copy, MAX_SECTION_LEN)) {
			free(data_copy);
			data_copy = NULL;
			fclose(fp_frost);
			fp_frost = NULL;
			fclose(fp_dest);
			fp_dest = NULL;
			return 1;
		}
		//save to file.
		if ((MAX_SECTION_LEN) != fwrite(data_copy, sizeof(unsigned char), MAX_SECTION_LEN, fp_dest)) {
			free(data_copy);
			data_copy = NULL;
			fclose(fp_frost);
			fp_frost = NULL;
			fclose(fp_dest);
			fp_dest = NULL;
			return 1;
		};		
	}

	//read, code and save the last section.
	last_section_len = data_num%MAX_SECTION_LEN;
	if (last_section_len != 0 ) {
		//read frost file section to memory.
		if (last_section_len != fread(data_copy, sizeof(unsigned char), last_section_len, fp_frost)) {
			free(data_copy);
			data_copy = NULL;
			fclose(fp_frost);
			fp_frost = NULL;
			fclose(fp_dest);
			fp_dest = NULL;
			return 1;
		}
		//decode the section.
		if (0 != decode_kernel(data_copy, last_section_len)) {
			free(data_copy);
			data_copy = NULL;
			fclose(fp_frost);
			fp_frost = NULL;
			fclose(fp_dest);
			fp_dest = NULL;
			return 1;
		}
		//save to file.
		if ((last_section_len) != fwrite(data_copy, sizeof(unsigned char), last_section_len, fp_dest)) {
			free(data_copy);
			data_copy = NULL;
			fclose(fp_frost);
			fp_frost = NULL;
			fclose(fp_dest);
			fp_dest = NULL;
			return 1;
		};
	}
	free(data_copy);
	data_copy = NULL;
	fclose(fp_frost);
	fp_frost = NULL;
	fclose(fp_dest);
	fp_dest = NULL;
	return 0;	
}

int decode_kernel(unsigned char *uc_str, unsigned int num)
{
	unsigned char *buffer;
	unsigned int i;

	if (0 != adjust(uc_str, num)) {
		return 1;
	}

	buffer = uc_str;
	for(i=0; i<num; i++){
		*buffer = clear(*buffer);
		buffer++;
	}
	buffer = NULL;
	return 0;
}

/*
*adjust bits's to there original positions in the file. 
*/
int adjust(unsigned char* file_copy, unsigned int num)
{
	unsigned int i;
	short int j;
	short int account;
	short int remainder;
	unsigned int quotient;
	
	unsigned int bit_team;
	unsigned int start_position_of_team[DISTURB_PARAM];
	unsigned int bit_position_before;
	unsigned int bit_position_after;
	unsigned char bit_value;

	unsigned char *buffer;

	buffer = (unsigned char *)malloc(sizeof(unsigned char) * num);
	memset(buffer, 0, num);
	if (buffer == NULL) {
		return 1;
	}
	
	//get the start positions of the teams of disturbed data.
	quotient = num*8/DISTURB_PARAM;
	remainder = num*8%DISTURB_PARAM;
	for(account=0; account<DISTURB_PARAM; account++){
		if (account < remainder) {
			start_position_of_team[account] = account + account*quotient;
		}
		else{
			start_position_of_team[account] = remainder + account*quotient;
		}
	}	

	//put every bit in new position
	for(i=0; i<num; i++){
		for(j=0; j<8; j++ ){
			bit_position_before = (i*8+j);
			for(account=DISTURB_PARAM-1; account>=0; account--){
				if (bit_position_before >= start_position_of_team[account]) {
					break;
				}
			}
			bit_team = account;
			//bit_team = (bit_position_before*DISTURB_PARAM)/(num*8);//need to prove.
			bit_position_after = (bit_position_before - start_position_of_team[bit_team]) * DISTURB_PARAM + bit_team;//need to check.
			bit_value = (file_copy[i] << j) & (0x80);//put the bit at the front of a char: b*******
			buffer[bit_position_after/8] |= bit_value >> (bit_position_after%8);		
		}
	}

	memcpy(file_copy, buffer, num);//void *memcpy( void *dest, const void *src, size_t count );
	free(buffer);
	buffer = NULL;
	return 0;
	
}
/*
 *	76543210-->34251607
 */
unsigned char clear(unsigned char bits)
{
	unsigned char temp1 = 0;//3*2*1*0*
	unsigned char temp2 = 0;//*4*5*6*7
	int i;

	for(i=0; i<8; i++){
		if (i < 4) {
			temp1 |= ((bits>>i) & 0x01) << (i*2+1);
		}
		else{
			temp2 |= ((bits>>i) & 0x01) << (2*(7-i));
		}
	}	
	return (temp1 | temp2);
}